package com.nuance.assignment.fileInfo.server.service;

import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;

import com.nuance.assignment.fileInfo.server.util.FileComparator;
import com.nuance.assignment.fileInfo.server.util.FileTransformer;
import com.nuance.assignment.fileInfo.shared.Constants;
import com.nuance.assignment.fileInfo.shared.FileDTO;
import com.nuance.assignment.fileInfo.shared.PageDTO;

/**
 * Service class that does the real job of handling file information
 * 
 * @author parag
 *
 */
@Service
@Validated
public class FileInfoServiceImpl implements FileInfoService {
	private static final Logger logger = Logger.getLogger(FileInfoServiceImpl.class);
	
	@Autowired
	private FileTransformer fileInfoTransformer;
	
	@Autowired
	private FileComparator fileComparator;
	
	/**
	 * File filter to select only directories
	 */
	private FileFilter directoryFilter = new FileFilter(){
		@Override
		public boolean accept(File file) {
			return file.isDirectory();
		}
	};
	
	/**
	 * File filter to select only directories starting with given name(case insensetive)
	 * 
	 * @author parag
	 *
	 */
	private class DirectoryNameFilter implements FileFilter{
		
		private String name = "";
		
		@Override
		public boolean accept(File file) {
			return directoryFilter.accept(file) && file.getAbsolutePath().toLowerCase().startsWith(name.trim().toLowerCase());
		}
		
		public void setName(String name) {
			this.name = name;
		}
	}
	private DirectoryNameFilter directoryNameFilter = new DirectoryNameFilter();
	
	/**
	 * Return suggestions related to specified path
	 * 
	 * @see com.nuance.assignment.fileInfo.server.service.FileInfoService#getSuggestions(java.lang.String, int)
	 */
	@Override
	public List<String> getSuggestions(String path, int size) {
		List<String> result = new ArrayList<>();
		File[] directories = null;
		if(StringUtils.hasText(path)){
			path = path.trim();
			File file = new File(path);
			if(file.exists() && file.isDirectory()){
				directories = file.listFiles(directoryFilter);
			}else{
				int index = path.lastIndexOf("/");
				if(index < 0){
					index = path.lastIndexOf("\\");
				}
				if(index < 0){
					directories = File.listRoots();
				}else{
					String newPath = path.substring(0, index + 1);
					file = new File(newPath);
					if(file.exists() && file.isDirectory()){
						directoryNameFilter.setName(path);
						directories = file.listFiles(directoryNameFilter);
					}else{
						directories = File.listRoots();
					}
				}
			}
		}else{
			directories = File.listRoots();
		}
		
		PageDTO<FileDTO> page = new PageDTO<>();
		page.setSort(Constants.PATH);
		page.setSize(size);
		
		sort(directories, page);
		directories = paginate(directories, page);
		for(File directory : directories){
			result.add(directory.getAbsolutePath());
		}
		return result;
	}
	
	/**
	 * Get files and directories within the directory denoted by specified path
	 * 
	 * @see com.nuance.assignment.fileInfo.server.service.FileInfoService#getChildren(java.lang.String, com.nuance.assignment.fileInfo.shared.PageDTO)
	 */
	public PageDTO<FileDTO> getChildren(String path, PageDTO<FileDTO> pageDTO) {
		logger.debug("Provided PATH is : "+path);
		File[] children = new File(path).listFiles();
		pageDTO.setTotal(children.length);
		
		sort(children, pageDTO);
		children = paginate(children, pageDTO);
		
		pageDTO.setContent(fileInfoTransformer.toDTO(children));
		return pageDTO;
	}
	
	/**
	 * Sort provided array with information given in pageDTO
	 * 
	 * @param files
	 * @param pageDTO
	 */
	private void sort(File[] files, PageDTO<FileDTO> pageDTO){
		fileComparator.setAscending(pageDTO.isAscending());
		fileComparator.setSortColumn(pageDTO.getSort());
		Arrays.sort(files, fileComparator);
	}
	
	/**
	 * Paginate the provided array with information given in pageDTO
	 * 
	 * @param files
	 * @param pageDTO
	 * @return
	 */
	private File[] paginate(File[] files, PageDTO<FileDTO> pageDTO){
		int from = pageDTO.getStart() > files.length ? files.length : pageDTO.getStart();
		int to = (pageDTO.getStart() + pageDTO.getSize()) > files.length ? files.length : (pageDTO.getStart() + pageDTO.getSize());
		return Arrays.copyOfRange(files, from, to);
	}
	
	/**
	 * Retrieve file system roots
	 * 
	 * @see com.nuance.assignment.fileInfo.server.service.FileInfoService#getRoots(com.nuance.assignment.fileInfo.shared.PageDTO)
	 */
	public PageDTO<FileDTO> getRoots(PageDTO<FileDTO> pageDTO){
		File[] roots = File.listRoots();
		pageDTO.setTotal(roots.length);
		
		sort(roots, pageDTO);
		roots = paginate(roots, pageDTO);
		
		pageDTO.setContent(fileInfoTransformer.toDTO(roots));
		return pageDTO;
	}
}